/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_SIGNAL_H
#define M_SIGNAL_H

#include <vector>

#include "refptr.h"
#include "slot.h"


namespace Engine
{
	/** 
	*   Signal realization.
	*/
	template<class Arg0=SlotHelper::unusable,
			 class Arg1=SlotHelper::unusable,
			 class Arg2=SlotHelper::unusable,
			 class Arg3=SlotHelper::unusable,
			 class Arg4=SlotHelper::unusable,
			 class Arg5=SlotHelper::unusable,
			 class Arg6=SlotHelper::unusable,
			 class Arg7=SlotHelper::unusable
			>
	class Signal
	{
	public:
		typedef RefPtr<SlotBase<Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7> > SlotBasePtr;		
		
		/** 
		*   Destructor.
		*/
		~Signal()
		{				
		}

		/** 
		*   Connect signal with some object and his member.
		*   Create slot object and store him in internal storage.
		*/
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT>::Function0 func)
		{
			Slot<ExecutePolicy,TT> *s = new Slot<ExecutePolicy,TT>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0>::Function1 func)
		{
			Slot<ExecutePolicy,TT,Arg0> *s = new Slot<ExecutePolicy,TT,Arg0>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1>::Function2 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2>::Function3 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3>::Function4 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4>::Function5 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5>::Function6 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6>::Function7 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		template<class ExecutePolicy,class TT>
		void Connect(TT* obj,typename Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>::Function8 func)
		{
			Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7> *s=new Slot<ExecutePolicy,TT,Arg0,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7>();
			s->Init(obj,func);
			connectedSlots.push_back(SlotBasePtr(s));
		}
		
		/** 
		*   Disconnect specified object(all his members) from signal.
		*/
		template<class TT>
		void Disconnect(TT* obj)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();

			for(;i!=e;++i)
			{
				if((*i)->GetObj() == obj)
				{					
					connectedSlots.erase(i);
					if(connectedSlots.empty())
					{
						return;
					}
					else
					{
						i=connectedSlots.begin();
						e=connectedSlots.end();
					}
				}
			}
		}
		/** 
		*   Disconnect all connected slots.
		*/
		void DisconnectAll()
		{
			connectedSlots.clear();
		}
		/** 
		*   Execute all slots connected to this signal.
		*/
		void operator()()
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute();
			}
		}
		void operator()(Arg0 arg)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg);
			}
		}
		void operator()(Arg0 arg0,Arg1 arg1)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1);				
			}
		}
		void operator()(Arg0 arg0,Arg1 arg1,Arg2 arg2)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1,arg2);
			}
		}
		void operator()(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1,arg2,arg3);
			}
		}

		void operator()(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1,arg2,arg3,arg4);
			}
		}

		void operator()(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1,arg2,arg3,arg4,arg5);
			}
		}


		void operator()(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1,arg2,arg3,arg4,arg5,arg6);
			}
		}

		void operator()(Arg0 arg0,Arg1 arg1,Arg2 arg2,Arg3 arg3,Arg4 arg4,Arg5 arg5,Arg6 arg6,Arg7 arg7)
		{
			typename std::vector<SlotBasePtr>::iterator i,e;
			i=connectedSlots.begin();
			e=connectedSlots.end();
			for(;i!=e;++i)
			{
				(*i)->Execute(arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7);
			}
		}
		/** 
		*   Return number of connected slots.
		*/
		size_t Connections()
		{
			return connectedSlots.size();
		}
	private:
		std::vector<SlotBasePtr> connectedSlots;///< holds all connected slots.
	};
}

#endif //SIGSLOT_H
